﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Red.Controls.TimeLine.Commands;
using Red.Controls.TimeLine.Core;
using Red.Controls.ViewModels;

namespace Red.Controls.TimeLine.ViewModels
{
    public class SectorsRoot : TimeLineItemBase
    {
        #region Data

        private readonly ObservableCollection<SectorObject> _items = new ObservableCollection<SectorObject>();
        private readonly TimeLineBackendProxy _backend = null;

        #endregion

        #region C-tor

        public SectorsRoot( TimeLineBackendProxy backend, TimeLineControler controler )
            : base( TimeLineId.InvalidId, controler )
        {
            _backend = backend;
        }

        #endregion

        #region Properties

        public IEnumerable<SectorObject> Children
        {
            get { return _items; }
        }

        public bool HasChildren
        {
            get { return _items.Count != 0; }
        }

        #endregion

        #region Public Methods

        public async Task OnSectorCreatedAsync( TimeLineId id )
        {
            _items.Add( await CreateSectorInternalAsync( id ) );
        }

        public Task OnSectorRemovedAsync( TimeLineId id )
        {
            SectorObject item = GetItemById( id );
            if ( item != null )
            {
                _items.Remove( item );
            }

            return Task.CompletedTask;
        }

        public override void RecalculateVisualsProperties()
        {
            foreach ( TimeLineItemBase trackObject in _items )
            {
                trackObject.RecalculateVisualsProperties();
            }
        }

        #endregion

        #region Internal methods

        internal async void Rebuild()
        {
            // Creates events or other objects here!

            _items.Clear();

            List<TimeLineId> items = new List<TimeLineId>();
            await _backend.GetSectorsAsync( items );

            if ( items.Count != 0 )
            {
                List<Task> tasks = new List<Task>();

                foreach ( TimeLineId id in items )
                {
                    Task<SectorObject> task = CreateSectorInternalAsync( id );
                    tasks.Add( task );

                    _items.Add( await task );
                }

                // wait for all children before continue
                await Task.WhenAll( tasks.ToArray() );
            }
        }

        #endregion

        #region Private methods

        private SectorObject GetItemById( TimeLineId id )
        {
            foreach ( SectorObject trackObject in _items )
            {
                if ( trackObject.Id == id )
                    return trackObject;
            }
            return null;
        }

        private async Task<SectorObject> CreateSectorInternalAsync( TimeLineId id )
        {
            TimeLineBackendProxy backend = _backend;
            string type = ( await backend.GetSectorTypeAsync( id ) ).Value;

            SectorObject item = null;
            if ( type == "Sector" )
            {
                item = new SectorObject( id, TimeLineControler );
            }

            if ( item == null )
            {
                throw new NullReferenceException();
            }

            item.DataModel = await backend.GetSectorRawDataAsync( id );
            return item;
        }

        #endregion        
    }

    public class SectorObject : TrackItemBase
    {
        #region Data

        private double _endTime = 0.0;
        private double _durationScreenWidth = 0.0;

        #endregion

        #region C-tor

        public SectorObject( TimeLineId id, TimeLineControler controler )
            : base( id, controler )
        {            
        }

        #endregion

        #region Properties

        public double EndTime
        {
            get { return _endTime; }
            private set
            {
                if ( _endTime != value )
                {
                    _endTime = value;
                    OnPropertyChanged();
                }
            }
        }

        public double DurationTime
        {
            get { return DataModel?.GetValue<double>( "DurationTime" ) ?? 0.0; }
            set
            {
                double time = TimeLineControler.SnapTime( value );
                if ( DataModel != null )
                {
                    DataModel.SetValue<double>( "DurationTime", time );
                }
            }
        }

        public double DurationScreenWidth
        {
            get { return _durationScreenWidth; }
            private set
            {
                if ( _durationScreenWidth != value )
                {
                    _durationScreenWidth = value;
                    OnPropertyChanged();
                }
            }
        }

        public override IEnumerable<RedMenuDataItem> Menu
        {
            get
            {
                List<RedMenuDataItem> menu = new List<RedMenuDataItem>();
                menu.Add( new RedMenuDataItem( new CommandWrapper( TimeLineCommands.DeleteSector, this ) ) );
                return menu;
            }
        }

        #endregion

        #region Public Methods

        public override void RecalculateVisualsProperties()
        {
            DurationScreenWidth = TimeLineControler.TimeToPixelRatio * DurationTime;
        }


        #endregion

        #region Protected Methods

        protected override void OnDataModelChanged()
        {
            base.OnDataModelChanged();
            DataModel.RegisterProperty( "DurationTime", "durationTime", OnDurationTimeChanged );
        }

        private void OnDurationTimeChanged( string propertyName, string value )
        {
            RecalculateVisualsProperties();
            RecalculateEndTime();

            OnPropertyChanged( propertyName );
        }

        protected override void OnActionTimeChanged( string propertyName, string value )
        {
            RecalculateEndTime();

            OnPropertyChanged( propertyName );
        }

        #endregion

        #region Private Methods

        private void RecalculateEndTime()
        {
            EndTime = ActionTime + DurationTime;
        }

        #endregion
    }
}
